/* $Id: AbstractDBModelCore.java 129 2011-08-26 05:27:43Z altdotua@gmail.com $ */

package alt.djudge.frontend.server.models;

import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import alt.djudge.frontend.server.HibernateUtil;
import alt.djudge.frontend.server.datatypes.AbstractEntry;


@SuppressWarnings("unchecked")
public abstract class AbstractDBModelCore<T extends AbstractEntry> implements CoreInterface<T>
{
	private static final Logger log = Logger.getLogger(AbstractDBModelCore.class);
	
	public T getEntryBySid(String sid)
	{
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		try
		{
			session.beginTransaction();
			Criteria crit = session.createCriteria(getEntryClass());
			crit.add(Restrictions.eq("sid", sid));
			List<T> result = crit.list();
			session.getTransaction().commit();
			if (result.size() > 0)
				return result.get(0);
		}
		catch (Exception e)
		{
			log.warn("getEntryBySid('" + sid +  "')", e);
		}
		finally
		{
			//if (session.getTransaction().isActive())
				//session.getTransaction().;
		}
		return null;
	}
	
	public T getEntry(Long entryId)
	{
		if (entryId == null)
			return null;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		try
		{
			session.beginTransaction();
			Criteria crit = session.createCriteria(getEntryClass());
			crit.add(Restrictions.eq("id", entryId));
			List<T> result = crit.list();
			session.getTransaction().commit();
			if (result.size() > 0)
				return result.get(0);
		}
		catch (NumberFormatException e)
		{
			log.warn("entry id must be numeric: " + entryId, e);
		}
		catch (Exception e)
		{
			log.warn("getEntry('" + entryId +  "')", e);
		}
		finally
		{
			//if (session.getTransaction().isActive())
				//session.getTransaction().rollback();
		}
		return null;
	}

	public boolean deleteEntry(Long entryId)
	{
	 	T entry = getEntry(entryId);
	 	if (null != entry)
	 	{
	 		return entry.delete();
	 	}
	 	else
	 	{
	 		log.warn("Trying to delete non-existent entry: " + getEntryClass().toString() + " id: " + entryId);
	 		return false;
	 	}
	}
	
	public List<T> getAllEntries()
	{
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		try
		{
			session.beginTransaction();
			Criteria crit = session.createCriteria(getEntryClass());
			crit.addOrder(getOrdering());
			return crit.list();
		}
		catch (Exception e)
		{
			log.warn("getAllEntries", e);
		}		
		finally
		{
		//	if (session.getTransaction().isActive())
			//	session.getTransaction().rollback();
		}
		return new Vector<T>();
	}
	
	public List<T> getAllActiveEntries()
	{
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		try
		{
			session.beginTransaction();
			Criteria crit = session.createCriteria(getEntryClass());
			crit.add(Restrictions.eq("active", true));
			crit.addOrder(getOrdering());
			return crit.list();
		}
		catch (Exception e)
		{
			log.warn("getAllActiveEntries", e);
		}		
		finally
		{
		//	if (session.getTransaction().isActive())
			//	session.getTransaction().rollback();
		}
		return new Vector<T>();		
	}
	
	public List<T> getEntries(Map<String, Object> query)
	{
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		try
		{
			session.beginTransaction();
			Criteria crit = session.createCriteria(getEntryClass());
			
    		Integer page = -1;
    		Integer count = 20;

    		try
    		{
	    		page = Integer.parseInt(query.get("page").toString());
    		} catch (Exception e) {}
    		try
    		{
	    		count = Integer.parseInt(query.get("count").toString());
    		} catch (Exception e) {}
    		query.remove("page");
    		query.remove("count");
    		
    		log.debug(this.getClass() + "PAGE = " + page + " COUNT = " + count);
    		
    		if (page != -1)
    		{
    			int pageSize = count;
    			crit.setFirstResult((page - 1) * pageSize);
    			crit.setMaxResults(pageSize);
    		}
			
			if (query.containsKey("order"))
			{
				String value = query.get("order").toString();
				query.remove("order");
				if (value.endsWith(" ASC"))
				{
					crit.addOrder(Order.asc(value.substring(0, value.length() - 4)));
				}
				else if (value.endsWith(" DESC"))
				{
					crit.addOrder(Order.desc(value.substring(0, value.length() - 5)));
				}
				else
				{
					crit.addOrder(Order.asc(value));
				}
			}
			else
			{
				// default ordering
				crit.addOrder(getOrdering());
			}
	
			for (String key : query.keySet())
				if (!key.equals("page") && !key.equals("order"))
				{
					int keyLen = key.length();
					Object value = query.get(key);
					if (key.endsWith(">="))
						crit.add(Restrictions.ge(key.substring(0, keyLen - 2), value));
					else if (key.endsWith("<="))
						crit.add(Restrictions.le(key.substring(0, keyLen - 2), value));
					else if (key.endsWith(">"))
						crit.add(Restrictions.gt(key.substring(0, keyLen - 1), value));
					else if (key.endsWith("<"))
						crit.add(Restrictions.lt(key.substring(0, keyLen - 1), value));
					else if (key.endsWith("="))
						crit.add(Restrictions.eq(key.substring(0, keyLen - 1), value));
					else if (key.endsWith("*"))
						crit.add(Restrictions.like(key.substring(0, keyLen - 1), value.toString(), MatchMode.ANYWHERE));
					else
						crit.add(Restrictions.eq(key, value));
				}

			return crit.list();
		}
		catch (Exception e)
		{
			log.warn("getEntries", e);
		}		
		finally
		{
	//		if (session.getTransaction().isActive())
		//		session.getTransaction().rollback();
		}
		return new Vector<T>();
	}
	
	public HashMap<Long, T> getEntriesMap()
	{
		List<T> list = getAllEntries();
		HashMap<Long, T> result = new HashMap<Long, T>();
		for (T entry: list)
			result.put(entry.getId(), entry);
		return result;
	}
	
	protected abstract Class<T> getEntryClass();
	
	protected abstract Order getOrdering();
	
	public void deleteAllEntries()
	{
		try
		{
			throw new Exception("Must implenent : deleteAll");
		}
		catch (Exception e)
		{
			log.fatal("Subclass must implement method deleteAllEntries", e);
		}
	}

	public T newEntry()
	{
		try
		{
			return getEntryClass().newInstance();
		}
		catch (Exception e)
		{
			log.fatal("Cannot create instance of " + getEntryClass().getSimpleName(), e);
		}
		return null;
	}

	@Override
	public CoreInterface<T> getAdapter()
	{
		return new CoreInterface<T>()
		{
			@Override
			public T getEntry(Long entryId)
			{
				return AbstractDBModelCore.this.getEntry(entryId);
			}
			
			@Override
			public List<T> getEntries(Map<String, Object> map)
			{
				return AbstractDBModelCore.this.getEntries(map);
			}
			
			@Override
			public List<T> getAllEntries()
			{
				return AbstractDBModelCore.this.getAllEntries();
			}
			
			@Override
			public void deleteAllEntries()
			{
				AbstractDBModelCore.this.deleteAllEntries();
			}

			@Override
			public CoreInterface<T> getAdapter()
			{
				return this;
			}

			@Override
			public boolean deleteEntry(Long entryId)
			{
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public T newEntry()
			{
				return this.newEntry();
			}
		};
	}
}
